package com.test;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * @ClassName HttpSSLHandler
 * @Description POST，GET请求的构建模式Handler
 * @author jie.dou
 * @Date 2017年3月14日 下午5:33:05
 * @version 1.0.0
 */
public class HttpSSLHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(HttpSSLHandler.class);
    
    /**
     * API接口URL
     */
    private String apiUrl;
    
    /**
     * 参数map
     */
    private Map<String, Object> params;
    
    /**
     * 请求头
     */
    private Map<String, Object> headers;
    
    /**
     * Socket 超时时间
     */
    private int SOCKET_TIMEOUT = 5000;
    
    /**
     * 连接超时时间
     */
    private int CONNECT_TIMEOUT = 5000;
    
    /**
     * 请求连接超时时间
     */
    private int CONNECTION_REQUEST_TIMEOUT = 5000;
    
    public static class Builder{
        
        private String apiUrl;
        
        private Map<String, Object> params;
        
        private Map<String, Object> headers;
        
        private int SOCKET_TIMEOUT;
        
        private int CONNECT_TIMEOUT;
        
        private int CONNECTION_REQUEST_TIMEOUT;
        
        public Builder(){}
 
        public Builder setApiUrl(String apiUrl) {
            this.apiUrl = apiUrl;
            return this;
        }
        
        public Builder setParams(Map<String, Object> params) {
            this.params = params;
            return this;
        }
        
        public Builder setHeaders(Map<String, Object> headers) {
            this.headers = headers;
            return this;
        }
        
        public Builder setSOCKET_TIMEOUT(int sOCKET_TIMEOUT) {
            this.SOCKET_TIMEOUT = sOCKET_TIMEOUT;
            return this;
        }
        
        public Builder setCONNECT_TIMEOUT(int cONNECT_TIMEOUT) {
            this.CONNECT_TIMEOUT = cONNECT_TIMEOUT;
            return this;
        }
        
        public Builder setCONNECTION_REQUEST_TIMEOUT(int cONNECTION_REQUEST_TIMEOUT) {
            this.CONNECTION_REQUEST_TIMEOUT = cONNECTION_REQUEST_TIMEOUT;
            return this;
        }
        
        public HttpSSLHandler build(){
            return new HttpSSLHandler(this);
        }
        
    }
    
    private HttpSSLHandler(Builder builder){
        this.apiUrl = builder.apiUrl;
        this.params = builder.params;
        this.headers = builder.headers;
        this.CONNECT_TIMEOUT = builder.CONNECT_TIMEOUT;
        this.CONNECTION_REQUEST_TIMEOUT = builder.CONNECTION_REQUEST_TIMEOUT;
        this.SOCKET_TIMEOUT = builder.SOCKET_TIMEOUT;
    }
       
    public String getApiUrl() {
        return apiUrl;
    }
    
    public Map<String, Object> getParams() {
        return params;
    }
    
    public Map<String, Object> getHeaders() {
        return headers;
    }
    
    public int getSOCKET_TIMEOUT() {
        return SOCKET_TIMEOUT;
    }
    
    public int getCONNECT_TIMEOUT() {
        return CONNECT_TIMEOUT;
    }
    
    public int getCONNECTION_REQUEST_TIMEOUT() {
        return CONNECTION_REQUEST_TIMEOUT;
    }

    /**
     * 发送 SSL POST 请求（HTTPS），K-V形式,并且带请求头
     * @return
     * @throws Exception
     */
    public String doPostSSL() throws Exception{
        CloseableHttpClient httpClient = getHttpsOrHttpCloseableHttpClient();
        HttpPost httpPost = new HttpPost(this.getApiUrl());
        CloseableHttpResponse response = null;
        String httpStr = null;
        try {
            if (this.getHeaders() != null && this.getHeaders().size() > 0) {
                for (Map.Entry<String, Object> param : this.getHeaders().entrySet()) {
                    httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
                }
            }
            RequestConfig requestConfig = RequestConfig
                    .custom()
                    .setSocketTimeout(this.getSOCKET_TIMEOUT())
                    .setConnectTimeout(this.getCONNECT_TIMEOUT())
                    .setConnectionRequestTimeout(this.getCONNECTION_REQUEST_TIMEOUT())
                    .build();
            httpPost.setConfig(requestConfig);
            List<NameValuePair> pairList = new ArrayList<NameValuePair>(this.getParams().size());
            for (Map.Entry<String, Object> entry : this.getParams().entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            logger.error("发生http异常==>doPostSSL Exception", e);
            throw e;
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (Exception e) {
                    logger.error("发生http关闭资源异常==>doPostSSL Exception", e);
                    throw e;
                }
            }
        }
        return httpStr;
    }
    
    /**
     * 发送 SSL POST 请求（HTTPS），K-V形式,并且带请求头,带头信息的https请求的GET请求
     * @return
     * @throws Exception
     */
    public String doGetSSL() throws Exception {
        CloseableHttpClient httpClient = getHttpsOrHttpCloseableHttpClient();
        URIBuilder ub = new URIBuilder();
        ub.setPath(this.getApiUrl());

        ArrayList<NameValuePair> pairs = covertParams2NVPS(this.getParams());
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build());
        for (Map.Entry<String, Object> param : this.getHeaders().entrySet()) {
            httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
        }

        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            // response.getStatusLine().getStatusCode();
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                // long len = entity.getContentLength();// -1 表示长度未知
                String result = EntityUtils.toString(entity);
                response.close();
                // httpClient.close();
                return result;
            }
        } catch (Exception e) {
            logger.error("发生http请求异常==>doGetSSL Exception", e);
            throw e;
        }
        return "";
    }
    
    private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
        ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
        }

        return pairs;
    }
   
   /**
    * 获取注册了http和https协议的CloseableHttpClient对象
    * 
    * @return
    * @throws NoSuchAlgorithmException
    * @throws KeyManagementException
    */
   private CloseableHttpClient getHttpsOrHttpCloseableHttpClient() throws Exception {
       // 采用绕过验证的方式处理https请求
       SSLContext sslcontext = createIgnoreVerifySSL();
       // 设置协议http和https对应的处理socket链接工厂的对象
       Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
               .register("http", PlainConnectionSocketFactory.INSTANCE)
               .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
       PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
       HttpClients.custom().setConnectionManager(connManager);

       // 创建自定义的httpclient对象
       CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connManager).build();
       return httpClient;
   }
   
   /************** 轻松把玩HttpClient之配置ssl，采用绕过证书验证实现https***START ************/
   /**
    * 绕过验证
    * 
    * @return
    * @throws NoSuchAlgorithmException
    * @throws KeyManagementException
    */
   public  SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
       SSLContext sc = SSLContext.getInstance("SSLv3");

       // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
       X509TrustManager trustManager = new X509TrustManager() {

           @Override
           public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                   String paramString) throws CertificateException {
           }

           @Override
           public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                   String paramString) throws CertificateException {
           }

           @Override
           public java.security.cert.X509Certificate[] getAcceptedIssuers() {
               return null;
           }
       };

       sc.init(null, new TrustManager[] {trustManager }, null);
       return sc;
   }
  
}

